package com.scrabbleMaster.lindo.model;

import java.util.ArrayList;
import java.util.List;

import com.lindo.Lindo;
import com.scrabbleMaster.utils.SparseMatrix;

/*
 * Ejemplo z max
 * 
 * con z = a + b
 * a < 10; 
 * b < 10;
 * a + b < 30
 */

public class Model {
	private List<Expression> constraints = new ArrayList<Expression>();
	private int direction;
	private Expression objective;
	private SparseMatrix<Double> sparseMatrix;
	
	private Model(int direction, Expression objective) {
		this.direction = direction;
		this.objective = objective;
	}
	/**  refactorizar en objetivo */
	public int getDirection() {
		return this.direction;
	}
	
	public double getObjectiveConstantValue() {
		return objective.getConstantValue();
	}
	
	public double[] getObjectiveCoeficients() {
		List<String> varNames = this.objective.getInvolvedVarNames();
		double[] coeficients = new double[varNames.size()]; 
		for (int i = 0; i < varNames.size(); i++) {
			coeficients[i] = this.objective.getCoeficientFor(varNames.get(i));
		}
		return coeficients;
	}
	
	public int getNonzeroCoeficientsQuantityInConstraints() {
		List<String> varNames = collectVariableNames();
		int zeroCoefficients = 0;
		for (Expression constraint : constraints) {
			zeroCoefficients += constraint.getNotInvolvedVarsQuantity(varNames);
		}
		return constraints.size() * varNames.size() - zeroCoefficients;
	}
	
	private List<String> collectVariableNames() {
		return this.objective.getInvolvedVarNames();
	}
	public static Model maximizer(Expression objective) {
		return new Model(Lindo.LS_MAX, objective);
	}
	public Model suchThat(List<Expression> constraints) {
		this.constraints = constraints;
		this.sparseMatrix = new SparseMatrix<Double>(getCompleteCoefficients(constraints));
		return this;
	}
	
	private List<List<Double>> getCompleteCoefficients(List<Expression> constraints) {
		List<List<Double>> values = new ArrayList<List<Double>>();
		for (Expression expression : constraints) {
			List<Double> coefficients = new ArrayList<Double>();
			for (String varName : this.getInvolvedVariables()) {
				coefficients.add(expression.getCoeficientFor(varName));
			}
			values.add(coefficients);
		}
		return values;
	}
	
	public static Model minimizer(Expression objective) {
		return new Model(Lindo.LS_MIN, objective);
	}
	public List<String> getInvolvedVariables() {
		return collectVariableNames();
	}
	public int getConstraintsSize() {
		return this.constraints.size();
	}
	public double[] getConstraintsLimits() {
		double[] limits = new double[constraints.size()];
		for (int i = 0; i < constraints.size(); i++) {
			limits[i] = constraints.get(i).getLimit();
		}
		return limits;
	}
	
	public String getConstraintsTypes() {
		StringBuffer buffer = new StringBuffer();
		for (Expression constraint : constraints) {
			buffer.append(constraint.getOperatorRepresentation());
		}
		return buffer.toString();
	}
	
	public double[] getNonzeroCoeficientsInConstraints() {
		List<Double> nonZeroCoefficientsByColumns = this.sparseMatrix.getNonZeroCoefficientsByColumns();
		return toPrimitive(nonZeroCoefficientsByColumns.toArray(new Double[0]));
	}
	
	private double[] toPrimitive(Double[] array) {
		double[] result = new double[array.length];
		for (int i = 0; i < array.length; i++) {
			result[i] = array[i];
		}
		return result;
	}
	
	public int[] getNonZeroColumnStartInConstraints() {
		//List<Integer> positions = new ArrayList<Integer>();
		int[] positions = new int[this.getInvolvedVariables().size() + 1];
		int count = 0;
		int i = 0;
		for (String varName : this.getInvolvedVariables()) {
			boolean added = false;
			for (Expression constraint : constraints) {
				if (constraint.getCoeficientFor(varName) != 0) {
					if (!added) {
						positions[count] = i;
						added = true;
					}
					i++;
				}
			}
			count++;
		}
		positions[count] = this.getNonzeroCoeficientsQuantityInConstraints();
		return positions;
	}
	public double[] getUpperVariablesBounds() {
		return null;
	}
	public double[] getLowerVariablesBounds() {
		return null;
	}
	
	public List<String> getConstraintNames() {
		List<String> names = new ArrayList<String>();
		for (Expression constraint : constraints) {
			names.add(constraint.getName());
		}
		return names;
	}
	public int[] getRowIndexForNonZeroCoefficients() {
		return toPrimitive( this.sparseMatrix.getRowIndex());
	}
	private int[] toPrimitive(List<Integer> rowIndex) {
		int[] value = new int[rowIndex.size()];
		for (int i = 0; i < rowIndex.size(); i++) {
			value[i] = rowIndex.get(i);
		}
		return value;
	}
	
	
}
